#include "FunTrace.h"
#include <http.h>
#include <aknglobalnote.h> 
#include "transitlink.h"
#include "UriUtils.h"

/*!max:re2c */ 

static void PrintNoteL(TRefByValue<const TDesC> aFormat, ...)
    {
    VA_LIST list;
    VA_START(list, aFormat);
    TBuf<256> buf;
    buf.FormatList(aFormat, list);
    VA_END(list);
	CAknGlobalNote* globalNote = CAknGlobalNote::NewLC();
	globalNote->ShowNoteL(EAknGlobalInformationNote, buf); 
    CleanupStack::PopAndDestroy(globalNote);
    }

// ================
// ================
// ================
void CTransitLinkRequestPtjp::ConstructL()
{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	iStopStart = NULL;
	iStopEnd = NULL;
	iRouteType = NULL;
	iBody.CreateL(4096);
	iPostData.CreateL(1024);
	iMrtStart.CreateL(0), iMrtStartDetail.CreateL(0), iMrtStartDist.CreateL(0);
	iMrtEnd.CreateL(0), iMrtEndDetail.CreateL(0), iMrtEndDist.CreateL(0);
	iTransactionSubmitted = EFalse;
}

CTransitLinkRequestPtjp* CTransitLinkRequestPtjp::NewLC()
{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	CTransitLinkRequestPtjp* ret = new (ELeave) CTransitLinkRequestPtjp;
	CleanupStack::PushL(ret);
	ret->ConstructL();
	return ret;
}

CTransitLinkRequestPtjp* CTransitLinkRequestPtjp::NewL()
{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	CTransitLinkRequestPtjp* ret = NewLC();
	CleanupStack::Pop(ret);
	return ret;
}

static void SetHeaderL(RHTTPSession& aSession, RHTTPHeaders aHeaders, TInt aHdrField, const TDesC8& aHdrValue)
  {
	RStringF valStr = aSession.StringPool().OpenFStringL(aHdrValue);
	CleanupClosePushL(valStr);
	THTTPHdrVal val(valStr);
	aHeaders.SetFieldL(aSession.StringPool().StringF(aHdrField,
		RHTTPSession::GetTable()), val);
	CleanupStack::PopAndDestroy();  // valStr
  }

void CTransitLinkRequestPtjp::SubmitL(RHTTPSession& aSession, const TDesC8& aStopStart, const TDesC8& aStopEnd, const TDesC8& aRouteType, MTransitLinkCallbackPtjp* aTarget)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	delete iStopStart;
	delete iStopEnd;
	delete iRouteType;
	iStopStart = aStopStart.AllocL();
	iStopEnd = aStopEnd.AllocL();
	iTarget = aTarget;
	iSession = &aSession;
	iRouteType = aRouteType.AllocL();
	SubmitL_PHASE1();
}

void CTransitLinkRequestPtjp::SubmitL_PHASE1()
	{
	iPhase = PHASE1;
	//_LIT8(kUriStr, "http://htpc/TL_DF/eguide_DF_2.php");
	_LIT8(kUriStr, "http://203.211.60.158/TL_DF/eguide_DF_2.php");
	_LIT8(kContentType, "application/x-www-form-urlencoded");
	TUriParser8 uri;
	uri.Parse( kUriStr );
	RStringF method = iSession->StringPool().StringF(HTTP::EPOST,RHTTPSession::GetTable());
	CleanupClosePushL(method);
	iMrtStart.Zero(), iMrtStartDetail.Zero(), iMrtStartDist.Zero();
	iMrtEnd.Zero(), iMrtEndDetail.Zero(), iMrtEndDist.Zero();
	iBody.Zero();
	iLastError = KErrNone;
	
	iPostData.Format(
		_L8("night=false&china=false&premium=false&park=false&alltime=false&startType=code&endType=code&routeMode=short&routeType=%S&startblkno=&startstreetname=&startbldg=&startMRT=%%23&startpostal=&startOption=on&startcode=%S&endblkno=&endstreetname=&endbldg=&endMRT=%%23&endpostal=&endOption=on&endcode=%S&typetravel=on&dist=400"),
		iRouteType, iStopStart, iStopEnd
		);
	iTransaction.Close();
	iTransaction = iSession->OpenTransactionL(uri, *this, method);
	RHTTPHeaders hdr = iTransaction.Request().GetHeaderCollection();
	SetHeaderL(*iSession, hdr, HTTP::EContentType, kContentType);
	iTransaction.Request().SetBody(*this);
	iTransaction.SubmitL();
	iTransactionSubmitted = ETrue;
	CleanupStack::PopAndDestroy(&method);
	}

TBool CTransitLinkRequestPtjp::GetNextDataPart(TPtrC8& aDataPart)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	aDataPart.Set( iPostData );
	return ETrue;
	}

void CTransitLinkRequestPtjp::ReleaseData()
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	}

TInt CTransitLinkRequestPtjp::OverallDataSize()
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	return iPostData.Length();
	//return KErrNotFound; // chunked transfer causes issues over my wi-fi, no idea why
	}

TInt CTransitLinkRequestPtjp::Reset()
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
		return KErrNotSupported;
	}


TBool CTransitLinkRequestPtjp::RequestPending() const
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	return iTransactionSubmitted;
	}

void CTransitLinkRequestPtjp::Cancel()
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	if(iTransactionSubmitted) iTransaction.Cancel();
	iTransactionSubmitted = EFalse;
    iTransaction.Close(); // POWERDOWN
	}

CTransitLinkRequestPtjp::~CTransitLinkRequestPtjp()
{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	delete iStopStart;
	delete iStopEnd;
	delete iRouteType;
	iTransaction.Close();
	iBody.Close();
	iPostData.Close();
	iMrtStart.Close(), iMrtStartDetail.Close(), iMrtStartDist.Close();
	iMrtEnd.Close(), iMrtEndDetail.Close(), iMrtEndDist.Close();
}

void CTransitLinkRequestPtjp::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	if(aEvent.iStatus < KErrNone) iLastError = aEvent.iStatus;
	
	switch(iPhase)
		{
		case PHASE1:
			MHFRunL_PHASE1(aTransaction, aEvent);
			break;
		case PHASE2:
			MHFRunL_PHASE2(aTransaction, aEvent);
			break;
		}
	
	}

// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
static void formUrlEncode(const TDesC8& aSource, RBuf8& aBuf)
	{
	static const TUint8 numToHex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
	for(const TUint8* p = aSource.Ptr() ; p != aSource.Ptr() + aSource.Length() ; ++p)
		{
		if(
				(*p >= 'a' && *p <= 'z')
				|| (*p >= 'A' && *p <= 'Z')
				|| (*p >= '0' && *p <= '9')
				|| (*p == '.') || (*p == '-') || (*p == '_')
				)
			{
			aBuf.Append(*p);			
			}
		else if (*p == ' ')
			{
			aBuf.Append('+');			
			}
		else
			{
			aBuf.Append('%');
			aBuf.Append( numToHex[*p >> 4] );
			aBuf.Append( numToHex[*p & 0x0F] );
			}
		}
	}


void CTransitLinkRequestPtjp::MHFRunL_PHASE1(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
		  switch (aEvent.iStatus)
		    {
				case THTTPEvent::EUnrecoverableError:
					iLastError = KErrNotReady; // for Symbian 5th ed + 
				  break;
		    case THTTPEvent::EGotResponseHeaders:
		      break;

		    case THTTPEvent::EGotResponseBodyData:
		    	{
			      MHTTPDataSupplier* body = aTransaction.Response().Body();
			      TPtrC8 part;	      
			      body->GetNextDataPart(part);
			      if(part.Length() + iBody.Length() > iBody.MaxLength() )
			    	  iBody.ReAllocL(part.Length() + iBody.Length());	    	  	    	  
		    	  iBody.Append(part);	    	  	    	  
			      body->ReleaseData();
		    	}		      
		      break;

		    case THTTPEvent::ESucceeded:
		    case THTTPEvent::EFailed:
		    	TInt statusCode = aTransaction.Response().StatusCode();
		    	Cancel();
		    	
				
				if(aEvent.iStatus == THTTPEvent::ESucceeded)
					{
	//=====================

	// workaround for re2c
	if(iBody.Length() + YYMAXFILL > iBody.MaxLength() )
	  iBody.ReAllocL(YYMAXFILL + iBody.Length());	    	  	    	  
	iBody.SetLength(YYMAXFILL + iBody.Length());
					
	typedef TUint8 YYCTYPE;
	const YYCTYPE* YYCURSOR = &iBody[0];
	const YYCTYPE* YYMARKER = &iBody[0];
	const YYCTYPE* YYCTXMARKER = &iBody[0];
	const YYCTYPE* YYLIMIT = &iBody[0] + iBody.Length();
	const YYCTYPE* beg;

	#define YYFILL(n) goto end;
	
	RBuf8 postData;
	postData.CreateL(4096);
	CleanupClosePushL(postData);

	RArray<TPtrC8> optionList;
	CleanupClosePushL(optionList);
	TBool optionSelected, buttonInput;
	
	TPtrC8 currentId, currentValue;

	RBuf8* target = NULL;
	RBuf buf;
	buf.CreateL(0);
	CleanupClosePushL(buf);
	
	std:
	/*!re2c
		'<input' { buttonInput = EFalse; currentValue.Set(KNullDesC8()); beg = YYCURSOR; goto input; }
		'<select' { optionList.Reset(); beg = YYCURSOR; goto select; }
		'<option' { optionSelected = EFalse; beg = YYCURSOR; goto option; }
		'</select'
		{
			if(postData.Length()) postData.Append(_L8("&"));
			formUrlEncode(currentId, postData);
			postData.Append(_L8("List="));
			for(TInt i = 0 ; i < 1 ; ++i)
				formUrlEncode(optionList[i], postData);
			for(TInt i = 1 ; i < optionList.Count() ; ++i)
			{
				formUrlEncode(_L8("|"), postData);
				formUrlEncode(optionList[i], postData);
			}
			goto std;
		}
		'<!--'
		{
			goto comment;
		}
		[^] { goto std; }
	*/
	
	comment:
	/*!re2c
		'-->'
		{
			goto std;
		}
		[^] { goto comment; }
	*/
	
	input:
	/*!re2c
		'id='[\'"][^\'"]*[\'"]
		{
			currentId.Set( beg + 4, (YYCURSOR - 1) - (beg + 4) );
			beg = YYCURSOR;
			goto input;
		}
		'value='[\'"][^\'"]*[\'"]
		{
			currentValue.Set( beg + 7, (YYCURSOR - 1) - (beg + 7) );
			beg = YYCURSOR;
			goto input;
		}
		'type='[\'"]'button'[\'"]
		{
			buttonInput = ETrue;
			beg = YYCURSOR;
			goto input;
		}
		'>'
		{
			if(currentValue.Length() && !buttonInput)
			{
				if(postData.Length()) postData.Append(_L8("&"));
				formUrlEncode(currentId, postData);
				postData.Append(_L8("="));
				formUrlEncode( currentValue, postData );
			}
			beg = YYCURSOR;
			goto std;
		}
		[^] { beg = YYCURSOR; goto input; }
	 */
	
	select:
	/*!re2c
		'id='[\'"][^\'"]*[\'"]
		{
			currentId.Set( beg + 4, (YYCURSOR - 1) - (beg + 4) );
			beg = YYCURSOR;
			goto select;
		}
		'>' { beg = YYCURSOR; goto std; }
		[^] { beg = YYCURSOR; goto select; }
	 */

	option:
	/*!re2c
		'value='[\'"][^\'"]*[\'"]
		{
			TPtrC8 value(TPtrC8( beg + 7, (YYCURSOR - 1) - (beg + 7)));
			if(value != _L8("-"))
				optionList.AppendL(TPtrC8( beg + 7, (YYCURSOR - 1) - (beg + 7)));
			beg = YYCURSOR;
			goto option;
		}
		'selected'
		{
			optionSelected = ETrue;
			beg = YYCURSOR;
			goto option;
		}
		'>'
		{
			if(optionSelected)
			{
				if(postData.Length()) postData.Append(_L8("&"));
				formUrlEncode(currentId, postData);
				postData.Append(_L8("="));
				formUrlEncode( optionList[optionList.Count()-1], postData);
			}
			beg = YYCURSOR;
			goto std;
		}
		[^] { beg = YYCURSOR; goto option; }
	 */
	
	
	end:
	; // required by the compiler
	
	
	{
		postData.Append(_L8("&specialSvc=null&busstopcode=")); // required by transitlink
	}
	
	iPostData.Swap(postData);
	
	CleanupStack::PopAndDestroy(&buf);
	CleanupStack::PopAndDestroy(&optionList);
	CleanupStack::PopAndDestroy(&postData);

	#undef YYFILL

	//=====================

					SubmitL_PHASE2();
	
					}
				else
					{
					HBufC8* savStopStart = iStopStart;
					HBufC8* savStopEnd = iStopEnd;
					iStopStart = NULL;
					iStopEnd = NULL;
					struct{ } iStopStart, iStopEnd; // to "remove" the variables from the scope
					CleanupStack::PushL(savStopStart);
					CleanupStack::PushL(savStopEnd);
					
					TInt tableIndex = 0, rowIndex = 0;
					RArray<TTransitLinkCell> row;
					RArray<TTransitLinkLine> lines;
					TTransitLinkLine line;
					CleanupClosePushL(row);
					CleanupClosePushL(lines);
					
					iTarget->MTransitLinkCallbackPtjpRunL(*savStopStart, *savStopEnd, aEvent.iStatus, iLastError, statusCode, tableIndex, rowIndex, row, lines);

					CleanupStack::PopAndDestroy(&lines);
					CleanupStack::PopAndDestroy(&row);
					
					CleanupStack::PopAndDestroy(savStopEnd);
					CleanupStack::PopAndDestroy(savStopStart);
					}
				
				
		      break;

		    case THTTPEvent::ERedirectedPermanently:
		      break;
		    	
		    case THTTPEvent::ERedirectedTemporarily:
		      break;
		    	
		    default:
		      break;
		    }
	
	}

void CTransitLinkRequestPtjp::SubmitL_PHASE2()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iPhase = PHASE2;
	_LIT8(kUriStr, "http://203.211.60.158/TL_DF/eguide_DF_3.php");
	_LIT8(kContentType, "application/x-www-form-urlencoded");
	TUriParser8 uri;
	uri.Parse( kUriStr );
	RStringF method = iSession->StringPool().StringF(HTTP::EPOST,RHTTPSession::GetTable());
	CleanupClosePushL(method);
	iBody.Zero();
	iLastError = KErrNone;
	iTransaction.Close();
	iTransaction = iSession->OpenTransactionL(uri, *this, method);
	RHTTPHeaders hdr = iTransaction.Request().GetHeaderCollection();
	SetHeaderL(*iSession, hdr, HTTP::EContentType, kContentType);
	iTransaction.Request().SetBody(*this);
	iTransaction.SubmitL();
	iTransactionSubmitted = ETrue;
	CleanupStack::PopAndDestroy(&method);
	}



void CTransitLinkRequestPtjp::MHFRunL_PHASE2(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	
	  switch (aEvent.iStatus)
	    {
			case THTTPEvent::EUnrecoverableError:
				iLastError = KErrNotReady; // for Symbian 5th ed + 
			  break;
	    case THTTPEvent::EGotResponseHeaders:
	      break;

	    case THTTPEvent::EGotResponseBodyData:
	    	{
		      MHTTPDataSupplier* body = aTransaction.Response().Body();
		      TPtrC8 part;	      
		      body->GetNextDataPart(part);
		      if(part.Length() + iBody.Length() > iBody.MaxLength() )
		    	  iBody.ReAllocL(part.Length() + iBody.Length());	    	  	    	  
	    	  iBody.Append(part);	    	  	    	  
		      body->ReleaseData();
	    	}		      
	      break;

	    case THTTPEvent::ESucceeded:
	    case THTTPEvent::EFailed:
	    	{
	    	TInt statusCode = aTransaction.Response().StatusCode();
	    	Cancel();

			HBufC8* savStopStart = iStopStart;
			HBufC8* savStopEnd = iStopEnd;
			iStopStart = NULL;
			iStopEnd = NULL;
			struct{ } iStopStart, iStopEnd; // to "remove" the variables from the scope
			CleanupStack::PushL(savStopStart);
			CleanupStack::PushL(savStopEnd);
			
			TInt tableIndex = 0, rowIndex = 0;
			RArray<TTransitLinkCell> row;
			RArray<TTransitLinkLine> lines;
			TTransitLinkLine line;
			CleanupClosePushL(row);
			CleanupClosePushL(lines);
			
			if(aEvent.iStatus == THTTPEvent::ESucceeded)
				{
//=====================
				
if(iBody.Length() + YYMAXFILL > iBody.MaxLength() )
  iBody.ReAllocL(YYMAXFILL + iBody.Length());	    	  	    	  
iBody.SetLength(YYMAXFILL + iBody.Length());

typedef TUint8 YYCTYPE;
const YYCTYPE* YYCURSOR = &iBody[0];
const YYCTYPE* YYMARKER = &iBody[0];
const YYCTYPE* YYCTXMARKER = &iBody[0];
const YYCTYPE* YYLIMIT = &iBody[0] + iBody.Length();
const YYCTYPE* beg;


#define YYFILL(n) goto end;

enum {S_HTML, S_TABLE, S_TR, S_TD} status = S_HTML;

std:
/*!re2c
	'<TABLE'[^>]*'class="solution"'[^>]*'>'
	{
		rowIndex = 0;
		status = S_TABLE;
		goto std;
	}
	'</TABLE>'
	{
		if(status == S_TABLE)
		{
			// notify end of table
			iTarget->MTransitLinkCallbackPtjpRunL(*savStopStart, *savStopEnd, aEvent.iStatus, iLastError, statusCode, tableIndex, -1, row, lines);
			tableIndex++;
			status = S_HTML;
		}
		goto std;
	}
	'<TR'[^>]*'>'
	{
		if(status == S_TABLE)
		{
			row.Reset();
			lines.Reset();
			status = S_TR;
		}
		goto std;
	}
	'</TR>'
	{
		if(status == S_TR)
		{
			iTarget->MTransitLinkCallbackPtjpRunL(*savStopStart, *savStopEnd, aEvent.iStatus, iLastError, statusCode, tableIndex, rowIndex, row, lines);
			rowIndex++;
			status = S_TABLE;
		}
		goto std;
	}
	'<TD'[^>]*'>'
	{
		if(status == S_TR)
		{
			row.AppendL( TTransitLinkCell() );
			row[row.Count() - 1].iLinesBeg = lines.Count();
			line.iShortcutType = TTransitLinkLine::SHORTCUT_NONE;
			beg = YYCURSOR;
			status = S_TD;
		}
		goto std;
	}
	'</TD>'
	{
		if(status == S_TD)
		{
			line.iText.Set(beg, YYCURSOR - 5 - beg);
			lines.AppendL( line );
			row[row.Count() - 1].iLinesEnd = lines.Count();
			status = S_TR;
		}
		goto std;
	}
	'<BR>'
	{
		if(status == S_TD)
		{
			line.iText.Set(beg, YYCURSOR - 4 - beg);
			lines.AppendL( line ); 
			beg = YYCURSOR;
		}
		goto std;
	}
	'viewBusServiceInfo('['"][^'"]+['"]','['"][^'"]+['"]')'
	{
		const YYCTYPE* begSvc;
		const YYCTYPE* endSvc = YYCURSOR-2;
		for(begSvc = endSvc - 1 ; begSvc[-1] != '\'' && begSvc[-1] != '\"' ; --begSvc);
		line.iShortcut.Set( begSvc, endSvc - begSvc );
		line.iShortcutType = TTransitLinkLine::SHORTCUT_BUSSERVICE;
		goto std;
	}
	'viewBusStopLoc('['"][^'"]+['"]')'
	{
		const YYCTYPE* begStop;
		const YYCTYPE* endStop = YYCURSOR-2;
		for(begStop = endStop - 1 ; begStop[-1] != '\'' && begStop[-1] != '\"' ; --begStop);
		line.iShortcut.Set( begStop, endStop - begStop );
		line.iShortcutType = TTransitLinkLine::SHORTCUT_BUSSTOP;
		goto std;
	}
	'<!--'
	{
		goto comment;
	}
	'</HTML>'
	{
		// notify end of all tables
		iTarget->MTransitLinkCallbackPtjpRunL(*savStopStart, *savStopEnd, aEvent.iStatus, iLastError, statusCode, -1, -1, row, lines);
		goto end;
	}
	[^] { goto std; }
*/

comment:
/*!re2c
	'-->'
	{
		goto std;
	}
	[^] { goto comment; }
*/

end:
; // required by the compiler

#undef YYFILL

//=====================
				}
			else
				iTarget->MTransitLinkCallbackPtjpRunL(*savStopStart, *savStopEnd, aEvent.iStatus, iLastError, statusCode, tableIndex, rowIndex, row, lines);
			
			CleanupStack::PopAndDestroy(&lines);
			CleanupStack::PopAndDestroy(&row);
			
			CleanupStack::PopAndDestroy(savStopEnd);
			CleanupStack::PopAndDestroy(savStopStart);
	    	}
	      break;

	    case THTTPEvent::ERedirectedPermanently:
	      break;
	    	
	    case THTTPEvent::ERedirectedTemporarily:
	      break;
	    	
	    default:
	      break;
	    }
	}


TInt CTransitLinkRequestPtjp::MHFRunError(TInt aError, RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	}

HBufC8* CTransitLinkRequestPtjp::LineToTextLC(const TDesC8& aString)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	RBuf8 buf;
	buf.CreateL( aString.Length() );
	CleanupClosePushL(buf);
	
	typedef TUint8 YYCTYPE;
	const YYCTYPE* YYCURSOR = &aString[0];
	const YYCTYPE* YYMARKER = &aString[0];
	const YYCTYPE* YYCTXMARKER = &aString[0];
	const YYCTYPE* YYLIMIT = &aString[0] + aString.Length();
	const YYCTYPE* beg;
	
	#define YYFILL(n) { goto end; }

	std:
	/*!re2c
		'<'[^>]*'>' { goto std; }
		([\r\n\t]|("&"[a-zA-Z]+";"))+ { if(buf.Length() && buf[buf.Length() - 1] != ' ') buf.Append(' '); goto std; }
		[^] { buf.Append(YYCURSOR[-1]); goto std; }
	*/
	end:
	;

	#undef YYFILL

	buf.Append(YYCURSOR, YYLIMIT - YYCURSOR);
	CleanupStack::Pop(&buf);
	HBufC8* sav = buf.AllocLC();
	buf.Close();
	return sav;	}
